package com.cloudbroker.bcs.platform.dav.middle.aspect;

import java.lang.reflect.Method;
import java.util.List;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import com.cloudbroker.bcs.common.annotation.MayLogin;
import com.cloudbroker.bcs.common.annotation.NoInvitationCode;
import com.cloudbroker.bcs.common.annotation.NoLogin;
import com.cloudbroker.bcs.common.annotation.NoRefreshSession;
import com.cloudbroker.bcs.common.annotation.RequiredPermissions;
import com.cloudbroker.bcs.common.auth.AuthUtil;
import com.cloudbroker.bcs.common.constants.LogFields;
import com.cloudbroker.bcs.common.exception.HSBCSErrInfoException;
import com.cloudbroker.bcs.common.model.Pagination;
import com.cloudbroker.bcs.common.util.PrintUtil;
import com.cloudbroker.bcs.common.util.ReflectionUtil;
import com.cloudbroker.bcs.common.util.StringUtil;
import com.cloudbroker.bcs.common.util.VOUtil;
import com.cloudbroker.bcs.common.util.ValueUtil;
import com.cloudbroker.bcs.common.vo.HSBCSVOBase;
import com.cloudbroker.bcs.csvc.uc.api.vo.request.base.UCForm;
import com.cloudbroker.bcs.csvc.uc.api.vo.response.UserInfoBackVo;
import com.cloudbroker.bcs.platform.dav.bizapi.constants.HSBCPSErrorCodes;
import com.cloudbroker.bcs.platform.dav.bizapi.constants.HSBCPSErrorCodes.ClientErr;
import com.cloudbroker.bcs.platform.dav.bizapi.constants.type.BrokerLoginType;
import com.cloudbroker.bcs.platform.dav.bizapi.constants.type.TermType;
import com.cloudbroker.bcs.platform.dav.bizapi.service.business.plugs.result.ExtendAppBuyedListData;
import com.cloudbroker.bcs.platform.dav.bizapi.service.business.plugs.vo.ExtendAppBuyedListDbForm;
import com.cloudbroker.bcs.platform.dav.common.config.ConfigValues;
import com.cloudbroker.bcs.platform.dav.common.constant.CacheKeys;
import com.cloudbroker.bcs.platform.dav.common.constant.SysContants;
import com.cloudbroker.bcs.platform.dav.common.constant.type.PlatformType;
import com.cloudbroker.bcs.platform.dav.common.util.CacheHelper;
import com.cloudbroker.bcs.platform.dav.common.util.SessionHelper;
import com.cloudbroker.bcs.platform.dav.middle.context.HSBCPSBizDataContext;
import com.cloudbroker.bcs.platform.dav.middle.entity.Broker;
import com.cloudbroker.bcs.platform.dav.middle.entity.BrokerManager;
import com.cloudbroker.bcs.platform.dav.middle.entity.Customer;
import com.cloudbroker.bcs.platform.dav.middle.entity.Tenant;
import com.cloudbroker.bcs.platform.dav.middle.entity.Whitelist;
import com.cloudbroker.bcs.platform.dav.middle.extservice.uc.UCenterService;
import com.cloudbroker.bcs.platform.dav.middle.model.CustomerCache;
import com.cloudbroker.bcs.platform.dav.middle.service.BrokerManagerService;
import com.cloudbroker.bcs.platform.dav.middle.service.BrokerService;
import com.cloudbroker.bcs.platform.dav.middle.service.CustomerService;
import com.cloudbroker.bcs.platform.dav.middle.service.ExtendAppService;
import com.cloudbroker.bcs.platform.dav.middle.service.TenantService;
import com.cloudbroker.bcs.platform.dav.middle.service.WhitelistService;

public class AuthCheckAspect {
    
    private static final String CLASS_NAME = AuthCheckAspect.class.getName();
    
    private static final Logger LOGGER = LoggerFactory.getLogger(CLASS_NAME);
    
    @Resource
    private WhitelistService whitelistService;
    @Resource
    private ExtendAppService extendAppService;
    @Resource
    private UCenterService uCenterService;
    @Resource
    private BrokerManagerService brokerManagerService;
    @Resource
    private BrokerService brokerService;
    @Resource
    private TenantService tenantService;
    @Resource
    private CustomerService customerService;
    
    public AuthCheckAspect() {
        PrintUtil.printComponentLoaded(LOGGER, CLASS_NAME);
    }
    
    public Object checkAuth(ProceedingJoinPoint pjp) throws Throwable {
        //先清空下线程
        clearBinding();
        // 在Spring的环境里，signature就是MethodSignature
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取Method
        Method method = methodSignature.getMethod();
        HSBCSVOBase baseInfo = (HSBCSVOBase) pjp.getArgs()[0];
        // 方法上没有申明@NoLogin且类上也没有申明@NoLogin，则检查登录状况
        if ((!method.isAnnotationPresent(NoLogin.class)
                && !method.getDeclaringClass().isAnnotationPresent(NoLogin.class) && !method
                    .isAnnotationPresent(MayLogin.class))
                || (method.isAnnotationPresent(MayLogin.class) && !StringUtil.isBlank(baseInfo.getUser_token()))) {
            // 聚合class和method上的权限，并缓存
            RequiredPermissions requiredPermissionsAnn = method.getAnnotation(RequiredPermissions.class);
            String[] requiredPermissions = null;
            if (null != requiredPermissionsAnn) {
                requiredPermissions = requiredPermissionsAnn.value();
            }
            Object result = null;
            // 检查登录及权限
            try {
                checkAndBindLoginInfo(
                        baseInfo,
                        requiredPermissions,
                        method.isAnnotationPresent(NoRefreshSession.class),
                        !method.isAnnotationPresent(MayLogin.class)
                                && method.isAnnotationPresent(NoInvitationCode.class));
            } catch (HSBCSErrInfoException e) {
                if (e.isErrorIn(HSBCPSErrorCodes.NOT_LOGGED, HSBCPSErrorCodes.NO_PERMISSION,
                        ClientErr.NO_SUCH_CLIENT)) {
                    result = VOUtil.genErrorResultVo(e.getErrorNo());
                    return result;
                }
                throw e;
            }
            
        }
        if (method.isAnnotationPresent(MayLogin.class)) {
            //优先判断broker_key
            String brokerKey = ValueUtil.getString(ReflectionUtil.getValue(baseInfo, "broker_key"));
            String tenantKey = ValueUtil.getString(ReflectionUtil.getValue(baseInfo, "tenant_key"));
            if (HSBCPSBizDataContext.getBrokerId() == null && !StringUtil.isBlank(brokerKey)) {
                Broker broker = brokerService.getByKey(brokerKey);
                if (broker == null) {
                    throw new HSBCSErrInfoException(ClientErr.BROKER_NOT_FIND);
                }
                HSBCPSBizDataContext.setBrokerId(broker.getBroker_id());
                HSBCPSBizDataContext.setTenantId(broker.getTenant_id());
            } else if (HSBCPSBizDataContext.getTenantId() == null && !StringUtil.isBlank(tenantKey)) {
                Tenant tenant = tenantService.getByKey(tenantKey);
                if (tenant == null) {
                    throw new HSBCSErrInfoException(ClientErr.TENANT_NOT_EXIST);
                }
                HSBCPSBizDataContext.setTenantId(tenant.getTenant_id());
            } else if (HSBCPSBizDataContext.getTenantId() == null && StringUtil.isBlank(brokerKey)
                    && StringUtil.isBlank(tenantKey)) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.PARAM_ERROR);
            }
        }
        // 执行业务
        return pjp.proceed();
        
    }
    
    private void checkAndBindLoginInfo(HSBCSVOBase baseInfo, String[] requiredPermissions, boolean noRefresh,
            boolean noInvitationCode) {
        CustomerCache sessionIdentity = checkSessionPermission(baseInfo, requiredPermissions, noRefresh);
        //访客 无需绑定所有信息
        String cacheId = String.valueOf(SessionHelper.getInstance().getGuestTokenFromKeys(
                sessionIdentity.getBrokerId()));
        String paramId = baseInfo.getUser_token();
        
        if (!(cacheId.equals(paramId) && sessionIdentity.getUserType().equals(SysContants.SESSION_USER_TYPE_FANS))) {
            bindUserInfo(sessionIdentity, noInvitationCode);
        }
        HSBCPSBizDataContext.setTenantId(sessionIdentity.getTenantId());
        HSBCPSBizDataContext.setUserCenterId(sessionIdentity.getUserCenterId());
        HSBCPSBizDataContext.setBrokerId(sessionIdentity.getBrokerId());
    }
    
    private void bindUserInfo(CustomerCache sessionIdentity, boolean noInvitationCode) {
        // 当前session是client用户
        bindClientInfo(sessionIdentity.getCustomer(), sessionIdentity.getBroker(), sessionIdentity.getBrokerId(),
                sessionIdentity.getClient_id(), sessionIdentity.getPlatformType(), noInvitationCode);
    }
    
    private void bindClientInfo(Customer customer, Broker broker, Long brokerId, Long clientId,
            Integer platformType, boolean noInvitationCode) {
        if (PlatformType.MANAGE.getValue().equals(platformType)) {
            if (broker == null) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
            HSBCPSBizDataContext.setBroker(broker);
            HSBCPSBizDataContext.setTenantId(broker.getTenant_id());
        } else if (PlatformType.MOBILE.getValue().equals(platformType)) {
            if (customer == null) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
            HSBCPSBizDataContext.setTenantId(customer.getTenant_id());
            HSBCPSBizDataContext.setCustomer(customer);
            if (!noInvitationCode) {
                if (brokerId != null) {
                    CacheHelper cacheHelper = CacheHelper.getInstance();
                    String rightKey = CacheKeys.BROKER_RIGHT_INVITATION_CODE;
                    String invitationCode1 = cacheHelper.hget(rightKey, String.valueOf(brokerId));
                    String customerKey = CacheKeys.CUSTOMER_INVITATION_CODE;
                    String field = customer.getCustomer_id() + "." + brokerId;
                    String invitationCode2 = cacheHelper.hget(customerKey, field);
                    if (invitationCode1 != null && !invitationCode1.isEmpty()
                            && !invitationCode1.equals(invitationCode2)) {
                        //白名单用户判断
                        Whitelist white = whitelistService.queryByBrokerIdAndMobile(brokerId, customer.getMobile());
                        if (white == null) {
                            throw new HSBCSErrInfoException(ClientErr.INVITATION_CODE_ERR);
                        }
                    }
                }
            }
        }
        // 保存用户对象到线程上下文
        HSBCPSBizDataContext.setBrokerId(brokerId);
        HSBCPSBizDataContext.setClientId(clientId);
        //重新加入到在线用户列表缓存
        if (customer != null) {
            SessionHelper.getInstance().addOneToBrokeyOnline(String.valueOf(brokerId),
                    String.valueOf(customer.getCustomer_id()));
        }
    }
    
    private CustomerCache checkSessionPermission(HSBCSVOBase param, String[] requiredPermissions, boolean noRefresh) {
        
        // 获取携带的access_token
        String access_token = param.getUser_token();
        // 获取会话身份对象
        CustomerCache identity = null;
        if (ConfigValues.IsUserCenter() && !SessionHelper.getInstance().isGuestUserToken(access_token)) {
            //从用户中心获取用户信息
            UCForm ucForm = new UCForm();
            ucForm.setUser_token(access_token);
            UserInfoBackVo info = uCenterService.funcUserinfo(ucForm);
            if (VOUtil.isSuccess(info)) {
                //得到缓存中的identity
                identity = SessionHelper.getInstance().fetchSessionIdentityByUcId(info.getUser_id(),
                        CustomerCache.class);
                if (identity == null) {
                    if ("2".equals(info.getUser_type())) {
                        //封装用户信息到缓存
                        BrokerManager manager = brokerManagerService.getManagerByUcId(info.getMobile(), "1234567",
                                access_token, info.getUser_id());
                        //存在投顾,需要校验此投顾是否删除
                        Broker broker = brokerService.get(String.valueOf(manager.getBroker_id()));
                        if (null == broker) {
                            throw new HSBCSErrInfoException(HSBCPSErrorCodes.NOT_LOGGED);
                        }
                        //用户中心过来的
                        identity = brokerManagerService.createNewBrokerIdentity(broker,
                                manager.getBroker_manager_id(), manager.getUser_id(),
                                manager.getBroker_flag() != 1, BrokerLoginType.WEB);
                    } else if ("3".equals(info.getUser_type())) {
                        //先查询数据库里面有没有如果没有的话创建一条customer
                        Customer customer = customerService.getCustomerByUCId(info);
                        identity = customerService.createNewFansIdentity(customer, null);
                    }
                    SessionHelper.getInstance().renewUCUserInfo(identity, info.getUser_id());
                }
            } else {
                LOGGER.error("用户中心调用失败！");
            }
        } else {
            identity = SessionHelper.getInstance().fetchSessionIdentityByAccess_token(access_token,
                    CustomerCache.class);
        }
        if (null == identity) {
            // 如果会话身份对象不存在则表示未登录或已超时
            throw new HSBCSErrInfoException(HSBCPSErrorCodes.NOT_LOGGED);
        }
        // 先不检查client权限(包括大V和粉丝)
        if (!SysContants.SESSION_USER_TYPE_CLIENT.equals(identity.getUserType())
                && !SysContants.SESSION_USER_TYPE_FANS.equals(identity.getUserType())) {
            // 检查是否拥有需要的权限
            if (!AuthUtil.hasPermission(identity.getPermissions(), requiredPermissions)) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
        }
        
        /**
         * 1.游客访问 验证访问组件是否需要登录
         * 2.粉丝访问 验证访问组件 DAV是否购买
         */
        if (SysContants.SESSION_USER_TYPE_FANS.equals(identity.getUserType())) {
            
            if (!validExtendApp(requiredPermissions, identity, "")) {
                throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
            }
            
            String cacheId = String.valueOf(SessionHelper.getInstance().getGuestTokenFromKeys(
                    identity.getBrokerId()));
            if (cacheId.equals(access_token)) {
                if (!validExtendApp(requiredPermissions, identity, SysContants.SESSION_USER_TYPE_GUEST)) {
                    throw new HSBCSErrInfoException(HSBCPSErrorCodes.NO_PERMISSION);
                }
            }
        }
        
        if (!noRefresh) {
            // 刷新会话，重置超时时间
            SessionHelper.getInstance().refreshSession(
                    PlatformType.getByValue(String.valueOf(identity.getPlatformType())), access_token, identity);
        }
        // 保存access_token到线程上下文
        HSBCPSBizDataContext.setAccess_token(access_token);
        MDC.put(LogFields.ACCESS_TOKEN, access_token);
        // 注意，这里没做clear
        // 当前是放在com.cloudbroker.bcs.common.aspect.LogAspect.log(ProceedingJoinPoint)中clear
        
        return identity;
    }
    
    public boolean validExtendApp(String[] codes, CustomerCache identity, String visitType) {
        if (null == identity.getBrokerId()) {
            return true;
        }
        
        if (SysContants.SESSION_USER_TYPE_GUEST.equals(visitType)) {
            String needLoginAppStr = SessionHelper.getInstance().getNeedLoginExtendAppsByBrokerId(
                    identity.getBrokerId());
            if (!StringUtil.isEmpty(needLoginAppStr)) {
                return AuthUtil.isNeedLoginExtendApps(codes, needLoginAppStr.toString().split(","));
            }
        } else {
            String appsStr = SessionHelper.getInstance().getExtendAppsByBrokerId(identity.getBrokerId());
            if (!StringUtil.isEmpty(appsStr)) {
                return AuthUtil.hasBuyedExtendApps(codes, appsStr.split(","));
            }
        }
        
        ExtendAppBuyedListDbForm queryDb = new ExtendAppBuyedListDbForm();
        queryDb.setBroker_id(String.valueOf(identity.getBrokerId()));
        queryDb.setExtend_app_status("1");
        queryDb.setTerm_type_num(String.valueOf(TermType.FANS.getVal()));
        queryDb.setDefault_sort("");
        
        Pagination page = new Pagination();
        page.setPage_no(1);
        page.setPage_size(100);
        
        List<ExtendAppBuyedListData> list = extendAppService.listWithPlugs(queryDb, page);
        if (list == null || list.isEmpty()) {
            return false;
        }
        StringBuffer buyedSb = new StringBuffer();
        for (ExtendAppBuyedListData app : list) {
            buyedSb.append(app.getCode());
            buyedSb.append(",");
        }
        buyedSb.setLength(buyedSb.length() - 1);
        
        SessionHelper.getInstance().clearNewExtendAppsByBrokerId(identity.getBrokerId(), buyedSb.toString());
        //SessionHelper.getInstance().clearNewNeedLoginExtendAppsByBrokerId(identity.getBrokerId(), needLoginSb.toString());
        
        String needLoginSb = SessionHelper.getInstance().getNeedLoginExtendAppsByBrokerId(identity.getBrokerId());
        if (SysContants.SESSION_USER_TYPE_GUEST.equals(visitType)) {
            return AuthUtil.isNeedLoginExtendApps(codes, needLoginSb.toString().split(","));
        }
        return AuthUtil.hasBuyedExtendApps(codes, buyedSb.toString().split(","));
    }
    
    private void clearBinding() {
        HSBCPSBizDataContext.removeClientProfile();
        HSBCPSBizDataContext.removeAccess_token();
        HSBCPSBizDataContext.removeBroker();
        HSBCPSBizDataContext.removeBrokerId();
        HSBCPSBizDataContext.removeCustomer();
        HSBCPSBizDataContext.removeTenantId();
        HSBCPSBizDataContext.clear();
    }
    
}
